package dao

import (
	"context"
	"errors"

	"bmember/pkg/rpc"

	"bmember/internal/object"
	"bmember/pkg/db/pgex"

	pay_rpc "bmember/api/pb/client/pay/api/pay"
	"bmember/internal/model"
)

// OrdersDao 订单相关dao
type OrdersDao interface {
	// CreateRechargeOrder 创建充值订单
	CreateRechargeOrder(ctx context.Context, order *model.RechargeOrder) (int64, error)
	// GetRechargeOrderByOrderNo 根据orderNo 获取订单
	GetRechargeOrderByOrderNo(ctx context.Context, orderNo string) (*model.RechargeOrder, error)
	// LockRechargeOrderByOrderNo 根据id 锁定订单
	LockRechargeOrderByOrderNo(ctx context.Context, orderNo string) (*model.RechargeOrder, error)
	// LockRechargeOrderById 根据orderNo 锁定订单
	LockRechargeOrderById(ctx context.Context, id int64) (*model.RechargeOrder, error)
	// UpdateRechargeOrderById 更新充值订单
	UpdateRechargeOrderById(ctx context.Context, order *model.RechargeOrder) error
	// UpdatePayOrderNoById 更新支付单号
	UpdatePayOrderNoById(ctx context.Context, order *model.RechargeOrder) error
	// UpdateRefundOrderNoById 更新退单单号
	UpdateRefundOrderNoById(ctx context.Context, order *model.RechargeOrder) error
	// CreatePayOrder 创建支付单
	CreatePayOrder(ctx context.Context, p *object.CreatePayOrderInfo) (*object.PlatformPrepay, error)
	// TriggerPayOrderSuccess 触发支付成功
	TriggerPayOrderSuccess(ctx context.Context, payOrderNo string) (*object.PayOrderDetail, error)
	// GetPayOrderDetail 查询支付结果
	GetPayOrderDetail(ctx context.Context, payOrderNo string) (*object.PayOrderDetail, error)
	// CancelPayOrder 取消支付单
	CancelPayOrder(ctx context.Context, payOrderNo string) (*object.PayOrderDetail, error)
}

type oDaoImpl struct {
	*DaoBase
	payClientPool *rpc.Pool
	name          string
}

var oDao OrdersDao

// InitOrderDao 初始化  dao 层
func InitOrderDao(payClientPool *rpc.Pool) {
	oDao = &oDaoImpl{
		DaoBase:       daoBase,
		payClientPool: payClientPool,
		name:          "orders_dao",
	}
}

// GetOrdersDao 获得订单 dao 层
func GetOrdersDao() OrdersDao {
	return oDao
}

// CreateRechargeOrder 创建充值订单
func (dao *oDaoImpl) CreateRechargeOrder(ctx context.Context, order *model.RechargeOrder) (int64, error) {
	if order == nil {
		return 0, nil
	}
	param := []interface{}{
		order.BrandId,
		order.StoreId,
		order.GroupId,
		order.CardId,
		order.MemberId,
		order.AccId,
		order.OrderNo,
		order.Amount,
		order.RefundAmount,
		order.Payed,
		order.Refunded,
		order.PayOrderNo,
		order.RefundOrderNo,
		order.PayedTime,
		order.RefundedTime,
		order.Status,
		order.OrderSource,
		order.MarketingInfo,
		order.Remark,
	}
	query := "INSERT INTO recharge_order (brand_id, store_id, group_id, card_id, member_id, acc_id, order_no, amount, refund_amount, " +
		"payed, refunded, pay_order_no, refund_order_no, payed_time, refunded_time, status, order_source, marketing_info, remark, " +
		"created_at, updated_at) VALUES " +
		"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW(),NOW())"
	aff, err := dao.db.PgExec(ctx, "id", query, param...)
	if err != nil {
		return 0, err
	}
	lastId, _ := aff.GetLastId()
	return lastId, nil
}

// GetRechargeOrderByOrderNo 根据orderNo 获取订单
func (dao *oDaoImpl) GetRechargeOrderByOrderNo(ctx context.Context, orderNo string) (*model.RechargeOrder, error) {
	query := "SELECT * FROM recharge_order WHERE order_no=? AND deleted_at=0"
	order := &model.RechargeOrder{}
	err := dao.db.Get(ctx, order, query, orderNo)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return order, err
}

// LockRechargeOrderByOrderNo 根据orderNo 锁定订单
func (dao *oDaoImpl) LockRechargeOrderByOrderNo(ctx context.Context, orderNo string) (*model.RechargeOrder, error) {
	query := "SELECT * FROM recharge_order WHERE order_no=? AND deleted_at=0 FOR UPDATE"
	order := &model.RechargeOrder{}
	err := dao.db.Get(ctx, order, query, orderNo)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return order, err
}

// LockRechargeOrderById 根据id 锁定订单
func (dao *oDaoImpl) LockRechargeOrderById(ctx context.Context, id int64) (*model.RechargeOrder, error) {
	query := "SELECT * FROM recharge_order WHERE id=? AND deleted_at=0 FOR UPDATE"
	order := &model.RechargeOrder{}
	err := dao.db.Get(ctx, order, query, id)
	if err != nil && errors.Is(err, pgex.ErrEmpty) {
		return nil, nil
	}
	return order, err
}

// UpdateRechargeOrderById 更新充值订单
func (dao *oDaoImpl) UpdateRechargeOrderById(ctx context.Context, order *model.RechargeOrder) error {
	if order == nil {
		return nil
	}
	param := []interface{}{
		order.Amount,
		order.RefundAmount,
		order.Payed,
		order.Refunded,
		order.PayOrderNo,
		order.RefundOrderNo,
		order.PayedTime,
		order.RefundedTime,
		order.Status,
		order.MarketingInfo,
		order.Remark,
		order.Id,
	}
	query := "UPDATE recharge_order SET amount=?, refund_amount=?, payed=?, refunded=?, pay_order_no=?, refund_order_no=?, payed_time=?, " +
		"refunded_time=?, status=?, marketing_info=?, remark=?, updated_at=NOW() WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, param...)
	return err
}

// UpdatePayOrderNoById 更新支付单号
func (dao *oDaoImpl) UpdatePayOrderNoById(ctx context.Context, order *model.RechargeOrder) error {
	if order == nil {
		return nil
	}
	query := "UPDATE recharge_order SET pay_order_no=?, updated_at=NOW() WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, order.PayOrderNo, order.Id)
	return err
}

// UpdateRefundOrderNoById 更新退单单号
func (dao *oDaoImpl) UpdateRefundOrderNoById(ctx context.Context, order *model.RechargeOrder) error {
	if order == nil {
		return nil
	}
	query := "UPDATE recharge_order SET refund_order_no=?, updated_at=NOW() WHERE id=? AND deleted_at=0"
	_, err := dao.db.Update(ctx, query, order.RefundOrderNo, order.Id)
	return err
}

// CreatePayOrder 创建支付单
func (dao *oDaoImpl) CreatePayOrder(ctx context.Context, p *object.CreatePayOrderInfo) (*object.PlatformPrepay, error) {
	if p == nil {
		return nil, nil
	}
	req := &pay_rpc.CreatePayOrderRequest{
		OrderKey:  p.OrderKey,
		SgId:      uint32(p.BrandId),
		AppId:     p.AppId,
		OpenId:    p.OpenId,
		BizStore:  uint32(p.StoreId),
		BizNo:     p.BizNo,
		Amount:    p.Amount,
		Submitted: true,
		Remark:    p.Remark,
		IsvAppId:  p.IsvAppId,
		IsvOpenId: p.IsvOpenId,
		CardInfo:  p.CardInfo.ToJson(),
	}
	switch p.BilinApp {
	case model.ChannelMallMINA:
		req.SourceType = pay_rpc.SourceType_SOURCE_WECHAT_PROGRAM
		req.BizType = pay_rpc.BizType_BIZ_RECHARGE
		req.PayType = pay_rpc.PayType_PAY_WECHAT
	case model.ChannelBgMINA:
		req.SourceType = pay_rpc.SourceType_SOURCE_WECHAT_PROGRAM
		req.BizType = pay_rpc.BizType_BIZ_RECHARGE
		req.PayType = pay_rpc.PayType_PAY_WECHAT
	case model.ChannelMallAlipay:
		req.SourceType = pay_rpc.SourceType_SOURCE_ALIPAY_PROGRAM
		req.BizType = pay_rpc.BizType_BIZ_RECHARGE
		req.PayType = pay_rpc.PayType_PAY_ALIPAY
	case model.ChannelMallByte:
		req.SourceType = pay_rpc.SourceType_SOURCE_BYTEDANCE_PROGRAM
		req.BizType = pay_rpc.BizType_BIZ_RECHARGE
		req.PayType = pay_rpc.PayType_PAY_BYTEDANCE
	default:
		return nil, model.InvalidOrderSorceType
	}
	conn, err := dao.payClientPool.Get(ctx)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = conn.Close()
	}()
	client := pay_rpc.NewPayClient(conn.ClientConn)
	res, err := client.CreatePayOrder(ctx, req)
	if err != nil {
		return nil, err
	}
	if res.Status != pay_rpc.PayOrderStatus_PAY_ORDER_STATUS_INIT {
		return nil, model.PayOrderCantBePayed
	}
	return &object.PlatformPrepay{
		PayOrderNo:  res.OrderNumber,
		PrepayInfo:  res.PrepayInfo,
		PayTypeName: res.PayTypeName,
	}, nil
}

// TriggerPayOrderSuccess 触发支付成功
func (dao *oDaoImpl) TriggerPayOrderSuccess(ctx context.Context, payOrderNo string) (*object.PayOrderDetail, error) {
	req := &pay_rpc.CommonOrderRequest{
		OrderNumber: payOrderNo,
	}
	conn, err := dao.payClientPool.Get(ctx)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = conn.Close()
	}()
	client := pay_rpc.NewPayClient(conn.ClientConn)
	res, err := client.TriggerPaySuccess(ctx, req)
	if err != nil {
		return nil, err
	}
	return object.PayOrderViewPayToOrderDetail(res), nil
}

// CancelPayOrder 取消支付单
func (dao *oDaoImpl) CancelPayOrder(ctx context.Context, payOrderNo string) (*object.PayOrderDetail, error) {
	req := &pay_rpc.CommonQueryRequest{OrderNumber: payOrderNo}
	conn, err := dao.payClientPool.Get(ctx)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = conn.Close()
	}()
	client := pay_rpc.NewPayClient(conn.ClientConn)
	res, err := client.CancelPayOrder(ctx, req)
	if err != nil {
		return nil, err
	}
	return object.PayOrderViewPayToOrderDetail(res), nil
}

// GetPayOrderDetail 查询支付结果
func (dao *oDaoImpl) GetPayOrderDetail(ctx context.Context, payOrderNo string) (*object.PayOrderDetail, error) {
	req := &pay_rpc.CommonOrderRequest{
		OrderNumber: payOrderNo,
	}
	conn, err := dao.payClientPool.Get(ctx)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = conn.Close()
	}()
	client := pay_rpc.NewPayClient(conn.ClientConn)
	res, err := client.GetPayOrderView(ctx, req)
	if err != nil {
		return nil, err
	}
	return object.PayOrderViewPayToOrderDetail(res), nil
}

// CreateRefundPayOrder 创建退款单
func (dao *oDaoImpl) CreateRefundPayOrder(ctx context.Context, p *object.CreateRefundOrderInfo) (*object.RefundOrderDetail, error) {
	if p == nil {
		return nil, nil
	}
	req := &pay_rpc.CreateRefundOrderRequest{
		OrderNumber:  p.PayOrderNo,
		EmpId:        uint32(p.EmpId),
		RefundAmount: p.RefundAmount,
		BizNo:        p.BizNo,
		Reason:       p.Reason,
	}
	conn, err := dao.payClientPool.Get(ctx)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = conn.Close()
	}()
	client := pay_rpc.NewPayClient(conn.ClientConn)
	res, err := client.CreateRefundOrder(ctx, req)
	if err != nil {
		return nil, err
	}
	return object.RefundOrderViewToRefundOrderDetail(res), nil
}

// RetryTriggerRefundOrder 重试退款单
func (dao *oDaoImpl) RetryTriggerRefundOrder(ctx context.Context, refundOrderNo string, empId int64, isMarkSuccess bool) (*object.RefundOrderDetail, error) {
	req := &pay_rpc.RefundRetryRequest{
		OrderNumber: refundOrderNo,
		IsMarked:    isMarkSuccess,
		EmpId:       uint32(empId),
	}
	conn, err := dao.payClientPool.Get(ctx)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = conn.Close()
	}()
	client := pay_rpc.NewPayClient(conn.ClientConn)
	res, err := client.RefundRetry(ctx, req)
	if err != nil {
		return nil, err
	}
	return object.RefundOrderViewToRefundOrderDetail(res), nil
}

// GetRefundOrder 查询退款单
func (dao *oDaoImpl) GetRefundOrder(ctx context.Context, refundOrderNo string) (*object.RefundOrderDetail, error) {
	req := &pay_rpc.CommonQueryRequest{OrderNumber: refundOrderNo}
	conn, err := dao.payClientPool.Get(ctx)
	if err != nil {
		return nil, err
	}
	defer func() {
		_ = conn.Close()
	}()
	client := pay_rpc.NewPayClient(conn.ClientConn)
	res, err := client.GetRefundOrder(ctx, req)
	if err != nil {
		return nil, err
	}
	return object.RefundOrderViewToRefundOrderDetail(res), nil
}
